/**
 * gwt-validator 
 *
 * Copyright (C) 2006  Nikolaus Rumm, Christian Simonutti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package gwt.validator.client;

import gwt.validator.client.action.Action;
import gwt.validator.client.check.Check;
import gwt.validator.client.exception.ValidationIssue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * The <code>Validator</code> class applies a given set of checks to a panel.
 * 
 * @author <a href="mailto:nikolaus.rumm@gmail.com">Nikolaus Rumm</a>
 */
public class Validator {

	/**
	 * The validation scopes that will be validated.
	 */
	private List scopes;

	/**
	 * Flag that indicates if the validator should stop validating the current
	 * validation scope if there has already been detected a validation issue.
	 */
	private boolean stopValidatingScopeOnFirstIssue;

	/**
	 * Constructor.
	 * 
	 * @param aScopes
	 *            The validation scopes
	 */
	public Validator(ValidationScope[] aScopes) {
		super();
		this.scopes = Arrays.asList(aScopes);
		this.stopValidatingScopeOnFirstIssue = true;
	}

	/**
	 * Performs the validation.
	 * 
	 * @return Flag that indicates that there were no validation issues
	 */
	public boolean validate() {
		boolean res = true;

		// reset the actions
		this.resetActions();

		Iterator it = this.scopes.iterator();
		while (it.hasNext()) {
			ValidationScope currentScope = (ValidationScope) it.next();

			// validate the scope
			if (validateScope(currentScope).size() > 0) {
				res = false;
			}
		}

		return res;
	}

	/**
	 * validates the given scope.
	 * 
	 * @param aScope
	 *            The validation scope to validate
	 * @return The global validation issues
	 */
	private Collection validateScope(ValidationScope aScope) {
		List globalIssues = new ArrayList();
		List localIssues = new ArrayList();

		// validate the children
		if (aScope.getChildValidationScopes() != null) {
			Iterator it = aScope.getChildValidationScopes().iterator();
			while (it.hasNext()) {
				ValidationScope currentChildScope = (ValidationScope) it.next();

				// evaluate the child scope
				Collection childIssues = this.validateScope(currentChildScope);
				globalIssues.addAll(childIssues);
			}
		}

		if (!this.isStopValidatingScopeOnFirstIssue()
				|| globalIssues.size() <= 0) {
			// evaluate the checkers and collect the local issues
			Iterator checkIt = aScope.getChecks().iterator();
			while (checkIt.hasNext()) {
				Check currentCheck = (Check) checkIt.next();

				// perform the local validation
				localIssues.addAll(currentCheck.validate());

				if (this.isStopValidatingScopeOnFirstIssue()
						&& localIssues.size() > 0) {
					break;
				}
			}
		}

		// call the local actions
		Iterator actionIt = aScope.getActions().iterator();
		while (actionIt.hasNext()) {
			Action currentAction = (Action) actionIt.next();

			if (localIssues.size() > 0) {
				Iterator issueIt = localIssues.iterator();
				while (issueIt.hasNext()) {
					ValidationIssue currentIssue = (ValidationIssue) issueIt
							.next();
					currentAction.onFailure(currentIssue);
				}
			} else {
				currentAction.onSuccess();
			}
		}

		// add the local issues to the global issues
		globalIssues.addAll(localIssues);

		// call the global actions
		if (aScope.getGlobalActions() != null) {
			Iterator globalActionIt = aScope.getGlobalActions().iterator();
			while (globalActionIt.hasNext()) {
				Action currentAction = (Action) globalActionIt.next();

				if (globalIssues.size() > 0) {
					Iterator issueIt = globalIssues.iterator();
					while (issueIt.hasNext()) {
						ValidationIssue currentIssue = (ValidationIssue) issueIt
								.next();
						currentAction.onFailure(currentIssue);
					}
				} else {
					currentAction.onSuccess();
				}
			}
		}

		return globalIssues;
	}

	public List getScopes() {
		return this.scopes;
	}

	/**
	 * Read-accessor to the validation configuration setting.
	 * 
	 * @return Flag that indicates if the validation of the current validation
	 *         scope should be interrupted if there has already been detected a
	 *         validation issue
	 */
	public boolean isStopValidatingScopeOnFirstIssue() {
		return this.stopValidatingScopeOnFirstIssue;
	}

	/**
	 * Sets the validation configuration setting.
	 * 
	 * @param aStopValidatingScopeOnFirstIssue
	 *            Flag that indicates if the validation of the current
	 *            validation scope should be interrupted if there has already
	 *            been detected a validation issue
	 */
	public void setStopValidatingScopeOnFirstIssue(
			boolean aStopValidatingScopeOnFirstIssue) {
		this.stopValidatingScopeOnFirstIssue = aStopValidatingScopeOnFirstIssue;
	}

	/**
	 * Helper method that resets all actions in all validation scopes.
	 */
	private void resetActions() {
		Iterator it = this.scopes.iterator();
		while (it.hasNext()) {
			ValidationScope currentScope = (ValidationScope) it.next();
			Iterator actionIt = currentScope.getActions().iterator();
			while (actionIt.hasNext()) {
				((Action) actionIt.next()).reset();
			}
		}
	}
}
